import 'dart:convert';
import 'dart:core';
import 'dart:core' as prefix0;
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:cookie_jar/cookie_jar.dart';

class HttpUtils {
  static const String BASE_URL = "https://www.wanandroid.com/";
  static const String VERSION = "1.0.0";

  List<CancelToken> _cancelTokenList;

  Dio _dio;

  ///3、 使用factory关键字，定义工厂构造函数，只会创建一个实例
  factory HttpUtils() => _getInstance();

  ///2、初始化对象
  static HttpUtils _getInstance() {
    return HttpUtils._internal();
  }

  ///1、定义类的内部匿名函数
  HttpUtils._internal() {
    if (_dio == null) {
      BaseOptions baseOptions = BaseOptions(

          ///请求基地址,可以包含子路径，如: "https://www.google.com/api/".
          baseUrl: BASE_URL,

          ///连接服务器超时时间，单位是毫秒
          connectTimeout: 5 * 1000,

          ///发送数据的超时时间(毫秒)
          sendTimeout: 5 * 1000,

          ///接收数据的最长时限
          receiveTimeout: 5 * 1000,

          /// 请求的Content-Type，默认值是"application/json; charset=utf-8".
          /// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
          /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,  这样[Dio]就会自动编码请求体.
          contentType:
              ContentType.parse("application/x-www-form-urlencoded").toString(),

          /// 默认值是 `JSON`, 当响应头中content-type为"application/json"时，dio 会自动将响应内容转化为json对象。
          /// 如果想以二进制方式接受响应数据，如下载一个二进制文件，那么可以使用 `STREAM`.
          /// 如果想以文本(字符串)格式接收响应数据，请使用 `PLAIN`.
          responseType: ResponseType.plain,

          ///设置一些请求头
          headers: {
            "version": VERSION,
          });

      _cancelTokenList=List();

      _dio = new Dio(baseOptions);

      ///添加拦截器
      _dio.interceptors
          .add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
        // 在请求被发送之前做一些事情
        print("\n================== 请求数据 ==========================");
        print("url = ${options.uri.toString()}");
        print("headers = ${options.headers}");
        print("params = ${options.data}");

        return options;
      }, onResponse: (Response response) async {
        //在返回响应数据之前做一些预处理
        print("\n================== 响应数据 ==========================");
        print("code = ${response.statusCode}");
        print("data = ${response.data}");
        print("responseType = ${response.request.responseType.toString()}");
        print("\n");

        return response;
      }, onError: (DioError e) async {
        // 当请求失败时做一些预处理
        print("\n================== 错误响应数据 ======================");
        print("type = ${e.type}");
        print("message = ${e.message}");
        print("\n");

        return e;
      }));

      ///cookie管理
      var cookieJar = CookieJar();
      _dio.interceptors.add(CookieManager(cookieJar));
    }
  }

  ///get请求
  Future<T> get<T>(path, {params, options, cancelToken}) async {

    if(cancelToken!=null){
      _cancelTokenList.add(cancelToken);
    }


    Response response = await _dio.get(path,
        queryParameters: params, options: options, cancelToken: cancelToken);

    return _response<T>(response);
  }

  ///post请求
  Future<T> post<T>(path, {params, options, cancelToken}) async {

    if(cancelToken!=null){
      _cancelTokenList.add(cancelToken);
    }

    Response response = await _dio.post(path,
        queryParameters: params, options: options, cancelToken: cancelToken);

    return _response<T>(response);
  }


  ///取消请求
  void cancel(CancelToken cancelToken){
    cancelToken.cancel();
  }

  ///取消所有的请求
  void cancelAll(){
    if(_cancelTokenList.isNotEmpty){
      _cancelTokenList.forEach((token) => token.cancel());
    }
  }

  Future<T> _response<T>(Response response) {
    if (response.statusCode == HttpStatus.ok) {
      try {
        var jsonMap = _decodeData(response);
        int code = jsonMap["errorCode"];
        var data = jsonMap["data"];
        var errorMsg = jsonMap["errorMsg"];
        if (code == 0) {
          return Future.value(data as T);
        } else {
          return Future.error(ArgumentError(errorMsg));
        }
      } on DioError catch (e) {
        return Future.error(e);
      }
    } else {
      return Future.error(DioError(
          request: response.request,
          response: response,
          type: DioErrorType.RESPONSE,
          error: "${response.statusCode} + 服务器异常"));
    }
  }

  Map<String, dynamic> _decodeData(Response response) {
    if (response == null ||
        response.data == null ||
        response.data.toString().isEmpty) {
      return new Map();
    }
    return json.decode(response.data.toString());
  }
}
